////////////////////////////////////////////////////////////////////
// Programme de compression/décompression CPT pour Chrono Cross
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
// Nemesis pour Terminus Traduction (tt_nemesis@yahoo.fr)
// Date de création :		05/08/03
// Dernière modification :	06/08/03
////////////////////////////////////////////////////////////////////

// Entete
#include "Compact.h"

// Type de données pour les fichiers
typedef struct {
	char *nom;
	int num;
} Cpt_TFich;

// Noms des fichiers
Cpt_TFich Cpt_Outs[CPT_FICHS_NB];
_int16 Cpt_Outs_Nb;

/*------------------------------------------------------------------
  Initialisation des fichiers du CPT
--------------------------------------------------------------------
 <- dir : chemin partiel des infos à charger
 <- fich : fichier d'infos à charger
------------------------------------------------------------------*/
void Cpt_Infos (char *dir, char *fich)
{
	// Fichier des infos
	FILE *infos_f;
	// Nom de l'iso (chemin)
	char infos_nom[CPT_NOM_MAX];
	// Numero de fichier
	int fich_deb,fich_fin,f;
	// Nom de fichier
	char fich_nom_complet[CPT_FICHS_NOM];
	char fich_nom[CPT_FICHS_NOM];
	char fich_ext[4];
	// Ligne
	char ligne[CPT_LIGNE_MAX];
	// Nombre de champs d'infos
	int result;

	// Ouverture du fichier des infos principales
	sprintf(infos_nom,"%s%s%s%s.%s",
		DIR_INFOS,DIR_INFOS_CPT,dir,fich,EXT_INFOS);
	infos_f=fopen(infos_nom,"rb");

	if (infos_f==NULL)
		Main_Message_S("Erreur d'ouverture des infos.\n","",ER);
	else {
		// Réinitialisation
		Cpt_Outs_Nb=0;
		// Récupération des noms de fichiers
		while (fgets(ligne,CPT_LIGNE_MAX,infos_f)!=NULL)
			// Cas d'un commentaire ou d'un blanc
			if (ligne[0]!=COMMENT) {
				result=sscanf(ligne,"%d-%d %3s %s",
					&fich_deb,&fich_fin,fich_ext,fich_nom);
				switch (result) {
				case 4:
					sprintf(fich_nom_complet,
						" (%s).%s",fich_nom,fich_ext);
					break;
				case 3:
					sprintf(fich_nom_complet,
						".%s",fich_ext);
					break;
				case 2:
					sprintf(fich_nom_complet,
						".%s",EXT_DUMP);
					break;
				default:
					Main_Message_S("Info erronee... ","",ER);
				}
				if (result>1)
					for(f=fich_deb;f<=fich_fin;f++) {
						Cpt_Outs[Cpt_Outs_Nb].nom=strdup(fich_nom_complet);
						Cpt_Outs[Cpt_Outs_Nb].num=f;
						Cpt_Outs_Nb++;
					}
			}
		// Fermeture du fichier des infos
		fclose(infos_f);
	}
}

/*------------------------------------------------------------------
  Réinitialisation des infos des CPT à NULL
------------------------------------------------------------------*/
void Cpt_Infos_Vide (void)
{
	int f;

	for(f=0;f<CPT_FICHS_NB;f++)
		if (Cpt_Outs[f].nom!=NULL) {
			free(Cpt_Outs[f].nom);
			Cpt_Outs[f].nom=NULL;
		}
}

/*------------------------------------------------------------------
  Décompression CPT d'un fichier
--------------------------------------------------------------------
 <- dir : dossier du fichier CPT à décompresser
 <- fich : fichier CPT à décompresser (sans extension pour dossier)
------------------------------------------------------------------*/
void Cpt_Decompression (char *dir, int fich)
{
	// Fichier Cpt à décompresser
	FILE *cpt_f;
	char cpt_nom[CPT_NOM_MAX];
	char *cpt_nom2;
	char cpt_nom_complet[CPT_NOM_MAX];
	// Fichier de sortie
	FILE *out_f;
	char out_nom[CPT_NOM_MAX];
	// Nom du dossier
	char dossier[CPT_NOM_MAX];
	// Adresses des fichiers
	_int32 adresses[CPT_FICHS_NB];
	_int32 adresses_nb;
	// Index des fichiers
	int f;
	// Position d'arrêt de copie
	_int32 pos;
	// Buffer
	char buffer[CPT_BUFFER];

	// Recherche du fichier CPT
	sprintf(cpt_nom,"%04d",fich);
	sprintf(dossier,"%s%s",DIR_FICH_IN,dir);
	Fichier_Cherche(dossier,cpt_nom,EXT_CPT,&cpt_nom2);
	sprintf(cpt_nom_complet,"%s%s",dossier,cpt_nom2);
	// Ouverture du fichier CPT
	cpt_f=fopen(cpt_nom_complet,"rb");

	// Fichier existant ?
	if (cpt_f==NULL)
		Main_Message_S("Impossible d'ouvrir le fichier.\n","",ER);
	else {
		// Chargement des paramètres de décompression
		Cpt_Infos(dir,cpt_nom);
		// Creation du dossier (s'il n'existe pas déjà)
		sprintf(dossier,"%s%s%s%s\\",
			DIR_FICH_IN,dir,ENT_DOSSIER,cpt_nom);
		mkdir(dossier);
		// Lecture du nombre de fichiers
		fread(&adresses_nb,sizeof(_int32),1,cpt_f);
		// Chargement des adresses
		for(f=0;f<=adresses_nb;f++)
			fread(&adresses[f],sizeof(_int32),1,cpt_f);
		// Décompression des fichiers
		for(f=0;f<Cpt_Outs_Nb && f<adresses_nb;f++) {
			// Ouverture du fichier extrait
			sprintf(out_nom,"%s%04d%s",dossier,f,Cpt_Outs[f].nom);
			out_f=fopen(out_nom,"wb");
			if (out_f==NULL)
				Main_Message_S("Impossible d'extraire.\n","",ER);
			else {
				// Placement dans le fichier compacté
				fseek(cpt_f,adresses[Cpt_Outs[f].num],SEEK_SET);
				// Extraction du fichier
				for(pos=adresses[Cpt_Outs[f].num];
					pos<=adresses[Cpt_Outs[f].num+1]-CPT_BUFFER;
					pos+=CPT_BUFFER)
				{
					fread(buffer,sizeof(char),CPT_BUFFER,cpt_f);
					fwrite(&buffer,sizeof(char),CPT_BUFFER,out_f);
				}
				// Attention à l'ordre pour
				// rester dans les positifs
				pos=(adresses[Cpt_Outs[f].num+1]
					-adresses[Cpt_Outs[f].num])%CPT_BUFFER;
				fread(buffer,sizeof(char),pos,cpt_f);
				fwrite(&buffer,sizeof(char),pos,out_f);
				// Fermeture du fichier extrait
				fclose(out_f);
			}
		}
		// Fermeture du CPT
		fclose(cpt_f);
		// Nettoyage des infos cpt
		Cpt_Infos_Vide();
	}
}

/*------------------------------------------------------------------
  DéCPTage d'un ensemble de fichiers
--------------------------------------------------------------------
 <- fich = ensemble des fichiers déterminés par des délimiteurs
	exemple : 1-4;5;8 = fichiers 1->5 + 8
------------------------------------------------------------------*/
void Cpt_D_Exec (char* fich) {
	// Numéros des fichiers à décompacter
	char* fichs_intervals[CPT_FICHS_INTER];
	int interval,i;
	// Type d'intervalle
	int result;
	// Bornes
	unsigned _int16 fich_deb,fich_fin,r;
	// Dossier/fichiers
	char* dossier;
	char* fichiers;

	Main_Message_S("Decompression CPT de %s... ",fich,OK);

	// Décomposition dossier/fichiers
	dossier=strtok(fich,SEPARATEUR);
	if (dossier[0]==NEUTRE)
		dossier[0]=0;
	fichiers=strtok(NULL,SEPARATEUR);
	
	// Décomposition des rooms en intervalles
	interval=0; // intervalle actuel
	fichs_intervals[interval]=strtok(fichiers,";");
	do
		interval++;
	while((fichs_intervals[interval]=strtok(NULL,";"))!=NULL);

	// Extension des intervalles
	for(i=0;i<interval;i++) {
		result=sscanf(fichs_intervals[i],"%d-%d",
			&fich_deb,&fich_fin);
		switch (result) {
		case 1:
			Cpt_Decompression(dossier,fich_deb);
			break;
		case 2:
			for (r=fich_deb;r<=fich_fin;r++)
				Cpt_Decompression(dossier,r);
			break;
		default:
			Main_Message_D("Erreur dans l'intervalle %d.\n",i+1,ER);
		}
	}
	Main_OK();
}

/*------------------------------------------------------------------
  Compression CPT de fichiers
--------------------------------------------------------------------
 <- dir : dossier du fichier CPT à compresser
 <- fich : fichier CPT à décompresser (sans extension pour dossier)
------------------------------------------------------------------*/
void Cpt_Compression (char *dir, int fich)
{
	// Fichier Cpt à recomposer et original
	FILE *cpt_out_f,*cpt_in_f;
	char cpt_nom[CPT_NOM_MAX];
	char *cpt_nom2;
	char cpt_nom_complet[CPT_NOM_MAX];
	// Fichiers d'entrée
	FILE *in_f;
	char in_nom[CPT_NOM_MAX];
	// Nom du dossier
	char dossier[CPT_NOM_MAX];
	// Adresse d'un fichier
	unsigned _int32 adresse;
	// Index des fichiers
	int f;
	// Buffer
	char buffer[CPT_BUFFER];
	// Entete du fichier CPT
	int result;

	// Recherche du fichier CPT
	sprintf(cpt_nom,"%04d",fich);
	sprintf(dossier,"%s%s",DIR_FICH_IN,dir);
	Fichier_Cherche(dossier,cpt_nom,EXT_CPT,&cpt_nom2);
	sprintf(cpt_nom_complet,"%s%s",dossier,cpt_nom2);
	cpt_in_f=fopen(cpt_nom_complet,"rb");

	// Fichier existant ?
	if (cpt_in_f==NULL)
		Main_Message_S("Impossible de trouver le fichier.\n","",ER);
	else {
		// Ouverture du fichier CPT de sortie
		sprintf(dossier,"%s%s",DIR_FICH_OUT,dir);
		sprintf(cpt_nom_complet,"%s%s",dossier,cpt_nom2);
		cpt_out_f=fopen(cpt_nom_complet,"wb");
		// Chargement des paramètres de décompression
		Cpt_Infos(dir,cpt_nom);
		// Accès au dossier
		sprintf(dossier,"%s%s%s%s\\",
			DIR_FICH_OUT,dir,ENT_DOSSIER,cpt_nom);
		// Lecture du nombre de fichiers d'origine
		fread(&Cpt_Outs_Nb,sizeof(_int32),1,cpt_in_f);
		// Ecriture du nombre de fichiers
		fwrite(&Cpt_Outs_Nb,sizeof(_int32),1,cpt_out_f);
		// Enregistrement des adresses
		adresse=(Cpt_Outs_Nb+2)*sizeof(_int32);
		// Ecriture de l'adresse du premier fichier
		fwrite(&adresse,sizeof(_int32),1,cpt_out_f);
		for(f=0;f<Cpt_Outs_Nb;f++) {
			// Ouverture du fichier à réinsérer
			sprintf(in_nom,"%s%04d%s",dossier,f,Cpt_Outs[f].nom);
			// Complétion du fichier source
			Fichier_Completer(in_nom);
			// Chargement des informations
			in_f=fopen(in_nom,"rb");
			fseek(in_f,0L,SEEK_END);
			adresse+=ftell(in_f);
			fclose(in_f);
			// Ecriture de l'adresse du fichier
			fwrite(&adresse,sizeof(_int32),1,cpt_out_f);
		};
		// Recompression des fichiers
		for(f=0;f<Cpt_Outs_Nb;f++) {
			// Ouverture du fichier à réinsérer
			sprintf(in_nom,"%s%04d%s",dossier,f,Cpt_Outs[f].nom);
			in_f=fopen(in_nom,"rb");
			if (in_f==NULL)
				Main_Message_S("Impossible d'inserer.\n","",ER);
			else {
				// Copie des données
				while((result=fread(buffer,sizeof(char),
					CPT_BUFFER,in_f))>0)
					fwrite(&buffer,sizeof(char),result,cpt_out_f);
				// Fermeture du fichier extrait
				fclose(in_f);
			}
		}
		// Fermeture des fichiers
		fclose(cpt_out_f);
		fclose(cpt_in_f);
		// Nettoyage des infos cpt
		Cpt_Infos_Vide();
	}
}

/*------------------------------------------------------------------
  ReCPTage d'un ensemble de fichiers
--------------------------------------------------------------------
 <- fich = ensemble des fichiers déterminés par des délimiteurs
	exemple : 1-4;5;8 = fichiers 1->5 + 8
------------------------------------------------------------------*/
void Cpt_C_Exec (char* fich) {
	// Numéros des fichiers à recompacter
	char* fichs_intervals[CPT_FICHS_INTER];
	int interval,i;
	// Type d'intervalle
	int result;
	// Bornes
	unsigned _int16 fich_deb,fich_fin,r;
	// Dossier/fichiers
	char* dossier;
	char* fichiers;

	Main_Message_S("Compression CPT de %s... ",fich,OK);

	// Décomposition dossier/fichiers
	dossier=strtok(fich,SEPARATEUR);
	if (dossier[0]==NEUTRE)
		dossier[0]=0;
	fichiers=strtok(NULL,SEPARATEUR);
	
	// Décomposition des rooms en intervalles
	interval=0; // intervalle actuel
	fichs_intervals[interval]=strtok(fichiers,";");
	do
		interval++;
	while((fichs_intervals[interval]=strtok(NULL,";"))!=NULL);

	// Extension des intervalles
	for(i=0;i<interval;i++) {
		result=sscanf(fichs_intervals[i],"%d-%d",
			&fich_deb,&fich_fin);
		switch (result) {
		case 1:
			Cpt_Compression(dossier,fich_deb);
			break;
		case 2:
			for (r=fich_deb;r<=fich_fin;r++)
				Cpt_Compression(dossier,r);
			break;
		default:
			Main_Message_D("Erreur dans l'intervalle %d.\n",i+1,ER);
		}
	}
	Main_OK();
}
